Field-Test Agent or PM Calls the Skill
User points to a project folder under <owner>'s Projects - General\<Project Name>\ and asks to "create the SOO" or "build the SOO checklist." Project must be at the commissioning-prep stage where 0-Checklists/ is being populated. Cowork session and project folder both must be mounted via request_cowork_directory before any I/O.
Locate the Controls Spec
Walk the project's contract-documents tree in priority order. Vendor submittals beat design specs; design specs beat combined drawing sets. Stop at the first source found.
<Project>/11-Controls/— controls submittal (Verasys, Niagara, ALC, Distech, Tracer). Richer on sequences than the design spec when present.<Project>/000-Contract Documents/Specifications/Mechanical Specs.pdf— the design controls spec. Most common source.<Project>/000-Contract Documents/Conformed/<combined>.pdf— combined A/M/P/E set. Large (>100 MB); fallback only.
stat → Blocks: 0 means the file isn't on disk.Hand the local path to Step 02.
Tell user: right-click the file in File Explorer → "Always keep on this device". Don't try alternative fetch paths.
Extract Spec Text (OCR if Needed)
Most UNVC mechanical specs are image-based scans. Sample-check the text layer before committing to an OCR pass that costs ~3 minutes per 86-page spec.
- Open the PDF with
pdfplumberand extract text from the middle page as a probe. - If
len(sample) ≥ 50per sampled page → text-extractable. Usepdfplumberorpdftotext -layout. Skip to Step 03. - If image-based → OCR via the bundled
scripts/ocr_spec.py. Two-pass: low-DPI (-r 150 -gray) full-spec scan to locate the SOO section, then high-DPI (-r 250) on identified pages for numeric accuracy.
Cheap text extraction. ~5 sec for 86 pages.
pdftoppm → tesseract with xargs -P 4. ~3 min for 86 pages.
pdfplumber.extract_text() returns empty strings page after page on scanned PDFs. Always sample-check first — if first three sampled pages return >50 chars each, the PDF has a text layer and you can skip OCR.
Find the SOO Section
The behavioral rules live under PART 3 — EXECUTION → §3.2 SEQUENCE OF OPERATION of the controls specification section. Identify the page range (typically 4–8 pages); everything downstream works from these pages only.
Search headers in priority order:
SECTION 23 09 33— Sequence of Operation (primary)SECTION 23 09 23— Direct Digital Control (DDC) for HVACSECTION 23 09 13— Instrumentation and Control Devices for HVACSECTION 23 09 00— Instrumentation and Control for HVACSECTION 25 95 00— Integrated Automation Control SequencesSECTION 25 90 00— Integrated Automation Sequence of Operations- Legacy MasterFormat fallback:
Section 15900 / 15915
grep -liE "sequence of operation|3\.2 SEQUENCE|electric and electronic control" ocr/page-*.txt
Identify Equipment Groups
Read the SOO text, list every equipment tag mentioned, and group by UNVC convention. The spec drives grouping — identical sequences share a tab; any sequence difference splits.
- Same-tag families with identical sequences → one tab.
P-1andP-2(paired hydronic pumps) → tabP-1 P-2 Pumps.EF-1, EF-2, EF-10on the same schedule → tabEF-1 EF-2 EF-10. - Class-level sequences → one tab per class. All VAV terminals →
VAV Terminals. Don't make 25 zone tabs. - Distinct sequences → separate tabs.
RT-1with economizer vsRT-2without → split. - One-off equipment → its own tab.
B-1 Boiler,CV-1 ERV,AC-1 Heat Pump. - Equipment mentioned without controls content → no tab. Surface in the user summary as "verify whether commissioning testing is needed."
/ \ ? * [ ] · forbidden first/last: apostrophe · case-insensitive uniqueness. P-1/P-2 Pumps won't save — write P-1 P-2 Pumps. If too long, abbreviate the class label, not the tags.
Apply the Strict Source Rule
The single most important rule of the skill: every test block must trace back to text in the spec. No inferred sequences. No "typical HVAC behaviors" from training data. No boilerplate filler. If the spec doesn't say it, it doesn't go in the workbook. Gaps are surfaced to the user, not invented.
For each equipment group, write one test block per distinct mode, sequence, alarm, setpoint, or interlock explicitly described in the SOO text. Each block has four lines:
- Mode — short label of what's being verified · e.g.
Building Schedule (Occupied / Unoccupied) - Operation — one sentence summarizing the spec's stated behavior
- Task — 1–3 sentences, active voice, what the field-test agent does. No instruments mentioned — we test behavior, not calibration.
- Expected result — what the agent observes if the system runs per spec
§3.3 Field Quality Control (calibration), §3.4 System Startup (zone naming, holidays, clock), §3.5 Adjusting (programming memory), §3.6 Closeout / Owner Training. These belong in the Contractor Readiness checklist, not the SOO.
Build the Workbook
Hand the equipment-groups dict + test blocks to scripts/build_soo.py. The script does the openpyxl heavy-lifting — one template clone per group, populate, save.
- Load
assets/Template.xlsx(or the path inUNVC_SOO_TEMPLATEenv override). - Copy the
Equipment 1placeholder once per equipment group BEFORE populating any sheet. Populating in place and then copying inherits the populated state — cross-tab contamination is a real, observed bug. - Rename each copy to its equipment group's tab name (apply the 31-char + forbidden-character rules).
- Populate row 1 (title), row 2 (context — manufacturer, BAS topology, key setpoints), and test blocks from row 4 down (4 rows per block: Mode 22pt / Operation 32pt / Task 60pt / Expected 48pt).
- Remove the original
Equipment 1placeholder. - Populate the
Projectsheet: name in A1, subtitle in A2, metadata (Builder/Developer, FT Agent, Date, Project Name) in rows 4–5, hyperlinked equipment index from row 7 onward.
openpyxl.copy_worksheet() copies the source sheet's current contents — not the empty template. Always clone ALL tabs from the empty template first, then populate.
Hyperlink(ref=…, location="'TabName'!A1", display=…) — stores in the canonical location attribute (internal link). Avoid cell.hyperlink = "#'Tab'!A1" which Excel treats as external.
Two-Round QC Pass
After the workbook saves, two QC passes — one mechanical, one independent. The independent reviewer never sees the build code, only the source spec and the populated workbook.
- File opens in formula + data-only modes
- Sheet names + order match equipment list
- No leftover
Equipment 1placeholder - Every block: Mode / Operation / Task / Expected result, in order
- No formula errors (
#REF!,#DIV/0!) - Project-sheet hyperlinks resolve to existing tabs
- Fresh general-purpose subagent
- Gets OCR'd spec + populated workbook only
- Compares every °F, ppm, %, GPM, CFM, time, schedule back to source
- Flags any inferred behavior
- Identifies spec sequences missing from the workbook
- Reviewer never sees build code
Save & Surface
Save to <Project>/0-Checklists/, then summarize the run for the user in concise prose. Don't list every test block — give them what they need to validate and move on.
Filename pattern: project folder name → spaces collapsed to underscores → _SOO_Verification.xlsx. e.g. "Murray City Public Works" → Murray_City_Public_Works_SOO_Verification.xlsx.
Summary covers:
- Equipment tabs created — count + names
- Total test blocks per tab
- Spec content explicitly excluded under the strict source rule, with the spec quote that triggered the exclusion
- Round 2 reviewer's flagged-missing items
- §3.4–3.6 startup/closeout content excluded as out-of-scope, with a note that it belongs in the Contractor Readiness checklist
- The
computer://link to the saved workbook
cp and openpyxl.save both fail with Permission denied. Save to a sandbox scratch path first, then either ask the user to close Excel or save under a different filename. Don't lose the rebuilt content to a transient lock.
SOO Verification Workbook
One .xlsx at <Project>/0-Checklists/: a Project header sheet with hyperlinked equipment index, plus one tab per equipment group containing every Mode / Operation / Task / Expected-result block that the spec mandates — strictly source-traceable, mechanically QC'd, and independently reviewed for content fidelity.
<ProjectName>_SOO_Verification.xlsx
qc_round2_report.md extraction_summary.md